import java.awt.*; // to get Graphics object.

public class WavePacketCanvas extends Canvas implements Runnable {
	// This should be two interfering sine waves
	// so we can see the difference between group velocity
	// and phase velocity.
	Image offscreen;  // This is the offscreen image so we can double buffer.
	WavePacket curve = null; // The curve object knows the shape of the sines.
	int imagewidth = 200, imageheight = 100;  // The canvas sizes.
	Thread animator = null;  // This thread will run this object.
	boolean please_stop = false;  // We only request a stop in case we are mid-draw.
	long startTime; // A guess at when we want the next redraw to begin.

	// The constructor makes the curve object with default values.
	// They can be resized.
	WavePacketCanvas() {
		imagewidth = 200; imageheight = 100;
		curve = new WavePacket(imagewidth,imageheight,12.0,2.0);
	}

	// An public function to allow people to change the velocity
	// of the longer wavelength.  We pass this on to our curve object.
	public void setVelocity(double velocity) {
		curve.setVelocity(velocity);
	}
	public void setFrequency(double velocity) {
		curve.setFrequency(velocity);
	}

	// These two inform the layout what our minimum requirements are.
	// Without these, you may get a zero size object.
	public Dimension getPreferredSize() {
		Dimension d = new Dimension(150,50);
		return d;
	}

	public Dimension getMinimumSize() {
		Dimension d = new Dimension(150,50);
		return d;
	}

	// Start the animation
	public void enable() {
		super.enable();
		// Start the thread.
        	animator = new Thread(this);
        	animator.start();
		repaint();
	}

	// Stop it.
	public void disable() { 
        	if (animator != null) animator.stop();
        	animator = null;
		super.disable();
	}

	// This method draws the background and  text at its current position.
	public void paint(Graphics g) {
    		//Dimension d = this.size();
        	update(g);
	}


	public void update( Graphics g ) {
		// If the screen is invalid, don't redraw.  Just copy from the
		// offscreen buffer.
		if (offscreen != null) {
	            g = this.getGraphics();
        	    g.drawImage(offscreen, 0, 0, this);
		} else {
		    g.clearRect(0,0,imagewidth,imageheight);
		}
	}

    // Stop and start animating on mouse clicks.
    public boolean mouseDown(Event e, int x, int y) {
	if (e.target==this) {
		toggle();
	}
        return true;
    }


    public void toggle() {
        // if running, stop it.  Otherwise, start it.
        if (animator != null) please_stop = true;
        else { please_stop = false; enable(); } 
    }

    // The body of the animator thread.
    public void run() {
        while(!please_stop) {
	    // Mark the current time in case we take too long to process.
	    startTime = System.currentTimeMillis()+100;
            Dimension d = this.size();
            
            // Make sure the offscreen image is created and is the right size.
            if ((offscreen == null) ||
                ((imagewidth != d.width) || (imageheight != d.height))) {
                // if (offscreen != null) offscreen.flush();
		// Sometimes the image sizes are zero or negative before the frame
		// is drawn.
		if (d.width>1 && d.height>1) {
	                imagewidth = d.width;
        	        imageheight = d.height;
		}
		// Create an image from the size of this canvas using a method
		// from the component class, I think.
                offscreen = createImage(imagewidth, imageheight);
		// Make our curve agree to the size.
		curve.setSize(imagewidth,imageheight);
            }
           
	    // Compute does nothing but increment the timestep.
            curve.compute();
	    // Get the offscreen image.
            Graphics g = offscreen.getGraphics();
            // Draw into the off-screen image.
	    g.clearRect(0,0,d.width,d.height);
	    curve.draw(g);
	    // Request that the system call paint sometime soon.
	    repaint();

            // wait a tenth of a second, then draw it again!
            try {
		Thread.sleep(Math.max(0,startTime-System.currentTimeMillis()));
	    } catch (InterruptedException e) { ; }
        }
        animator = null;
    }
}
